﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;

namespace BesAsm.Emgaats.Maintenance
{
    internal class Configuration : IDisposable
    {
        private string _filePath;
        private string _demLayerFile;
        private string _masterSdeFile;
        private XPathDocument _xPath;
        private FieldMapping _fieldMapping;
        private IFeatureClass _nodeClass;
        private IFeatureClass _linkClass;
        private IFeatureClass _dmeLinkClass;
        private IRaster _dem;
        private bool _disposed = false;

        public Configuration()
        {
            _filePath = EmgaatsMaintenanceExtension.GetDataFilePath("EmgaatsToolConfiguration.xml");
            _masterSdeFile = EmgaatsMaintenanceExtension.GetDataFilePath("MasterLinks.sde");
            _demLayerFile = EmgaatsMaintenanceExtension.GetDataFilePath("DEM.lyr");
        }

        #region IDisposable Members

        ~Configuration()
        {
            _Dispose(false);
        }

        public void Dispose()
        {
            _Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void _Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    //Managed classes
                }

                //Unmanaged classes
                EmgaatsMaintenanceExtension.ReleaseComObject(_nodeClass);
                _nodeClass = null;
                EmgaatsMaintenanceExtension.ReleaseComObject(_linkClass);
                _linkClass = null;
                EmgaatsMaintenanceExtension.ReleaseComObject(_dmeLinkClass);
                _dmeLinkClass = null;
                EmgaatsMaintenanceExtension.ReleaseComObject(_dem);
                _dem = null;

                _disposed = true;
            }
        }

        #endregion

        
        public string ConfigurationFile
        {
            get
            {
                return _filePath;
            }
        }

        public string DemLayerFile
        {
            get
            {
                return _demLayerFile;
            }
        }

        public string MasterSdeFile
        {
            get
            {
                return _masterSdeFile;
            }
        }

        public IFeatureClass MasterLinksEditClass
        {
            get
            {
                if (_linkClass == null)
                {
                    IFeatureWorkspace workspace = _GetEmgaatsWorkspace() as IFeatureWorkspace;
                    _linkClass = workspace.OpenFeatureClass("ASM_MASTER_RPT_SDE.GIS.LINKS");
                }
                return _linkClass;
            }
        }

        public IFeatureClass MasterNodesEditClass
        {
            get
            {
                if (_nodeClass == null)
                {
                    IFeatureWorkspace workspace = _GetEmgaatsWorkspace() as IFeatureWorkspace;
                    _nodeClass = workspace.OpenFeatureClass("ASM_MASTER_RPT_SDE.GIS.NODES");
                }
                return _nodeClass;
            }
        }

        public IFeatureClass DmeLinksClass
        {
            get
            {
                if (_dmeLinkClass == null)
                {
                    IFeatureWorkspace workspace = _GetHubWorkspace() as IFeatureWorkspace;
                    _dmeLinkClass = workspace.OpenFeatureClass("EGH_PUBLIC.ARCMAP_ADMIN.collection_lines_bes_pdx");
                }
                return _dmeLinkClass;
            }
        }

        public IRaster DEM
        {
            get
            {
                if (_dem == null)
                {
                    ILayerFile layerFile = new LayerFileClass();
                    if (layerFile.get_IsPresent(_demLayerFile) && layerFile.get_IsLayerFile(_demLayerFile))
                    {
                        layerFile.Open(_demLayerFile);
                        IRasterLayer layer = layerFile.Layer as IRasterLayer;
                        if (layer != null)
                        {
                            _dem = layer.Raster;
                        }
                    }
                }
                return _dem;
            }
        }

        public FieldMapping LinkFieldMapping
        {
            get
            {
                if (_fieldMapping == null)
                {
                    FieldMapping mapping = new FieldMapping();

                    XPathNavigator navigator = _XPath.CreateNavigator();
                    XPathNodeIterator nodes = navigator.Select("/Emgaats/Maintenance/LinkClass/FieldMappings/Field");
                    if (nodes.Count > 0)
                    {
                        IFeatureClass linksClass = MasterLinksEditClass;

                        for (int i = 0; i < nodes.Count; i++)
                        {
                            nodes.MoveNext();
                            XPathNavigator fieldNavigator = nodes.Current;
                            string fieldName = fieldNavigator.GetAttribute("name", string.Empty);
                            if (!string.IsNullOrEmpty(fieldName))
                            {
                                int fieldIndex = linksClass.FindField(fieldName);
                                if (fieldIndex > -1)
                                {
                                    Field field = new Field(fieldName);
                                    field.FieldType = _GetFieldType(linksClass.Fields.get_Field(fieldIndex));

                                    if (fieldNavigator.MoveToFirstAttribute())
                                    {
                                        _ExtractAttribute(fieldNavigator, field);
                                        while (fieldNavigator.MoveToNextAttribute())
                                        {
                                            _ExtractAttribute(fieldNavigator, field);
                                        }
                                        fieldNavigator.MoveToParent();
                                    }

                                    // Value mappings and valid values, if present
                                    if (fieldNavigator.MoveToFirstChild())
                                    {
                                        _ExtractValue(fieldNavigator, field);
                                        while (fieldNavigator.MoveToNext())
                                        {
                                            _ExtractValue(fieldNavigator, field);
                                        }
                                        fieldNavigator.MoveToParent();
                                    }

                                    mapping.AddField(field);
                                }
                            }
                        }
                    }

                    _fieldMapping = mapping;
                }
                return _fieldMapping;
            }
        }

        private void _ExtractValue(XPathNavigator fieldNavigator, Field field)
        {
            if (fieldNavigator.Name == "MappedValue")
            {
                string sourceValue = fieldNavigator.GetAttribute("dmeValue", string.Empty);
                if (!string.IsNullOrEmpty(sourceValue))
                {
                    field.UseValueMapping = true;
                    field.ValueMapping[sourceValue] = fieldNavigator.ValueAs(field.FieldType);
                }
            }
            else if (fieldNavigator.Name == "ValidValue")
            {
                field.AddValidValue(fieldNavigator.ValueAs(field.FieldType));
            }
            else if (fieldNavigator.Name == "MaxValue")
            {
                bool inclusive = true;
                string inclusiveText = fieldNavigator.GetAttribute("inclusive", string.Empty);
                if (!string.IsNullOrEmpty(inclusiveText))
                    inclusive = Convert.ToBoolean(inclusiveText);

                field.SetMaxValue(fieldNavigator.ValueAsDouble, inclusive);
            }
            else if (fieldNavigator.Name == "MinValue")
            {
                bool inclusive = true;
                string inclusiveText = fieldNavigator.GetAttribute("inclusive", string.Empty);
                if (!string.IsNullOrEmpty(inclusiveText))
                    inclusive = Convert.ToBoolean(inclusiveText);

                field.SetMinValue(fieldNavigator.ValueAsDouble, inclusive);
            }
        }

        private void _ExtractAttribute(XPathNavigator fieldNavigator, Field field)
        {
            switch (fieldNavigator.Name)
            {
                case "name":
                    field.Name = fieldNavigator.Value;
                    break;
                case "ignore":
                    field.Ignore = fieldNavigator.ValueAsBoolean;
                    break;
                case "hide":
                    field.Hidden = fieldNavigator.ValueAsBoolean;
                    break;
                case "dmeField":
                    field.SourceField = fieldNavigator.Value;
                    break;
                case "special":
                    field.UseSpecialLogic = fieldNavigator.ValueAsBoolean;
                    break;
                case "autoUpdate":
                    field.AutoUpdate = fieldNavigator.ValueAsBoolean;
                    break;
                case "validate":
                    field.Validate = fieldNavigator.ValueAsBoolean;
                    break;
                case "default":
                    field.UseDefault = true;
                    if(fieldNavigator.Value.ToLower() == "null")
                        field.DefaultValue = DBNull.Value;
                    else
                        field.DefaultValue = fieldNavigator.ValueAs(field.FieldType);
                    break;
                default:
                    System.Diagnostics.Debug.Assert(true, string.Format("Unknown attribute found in configuration xml: {0}", fieldNavigator.Name));
                    break;
            }
        }

        private Type _GetFieldType(IField esriField)
        {
            switch (esriField.Type)
            {
                case esriFieldType.esriFieldTypeBlob:
                    return typeof(byte[]);
                case esriFieldType.esriFieldTypeDate:
                    return typeof(DateTime);
                case esriFieldType.esriFieldTypeDouble:
                    return typeof(double);
                case esriFieldType.esriFieldTypeGeometry:
                    return typeof(object);
                case esriFieldType.esriFieldTypeGlobalID:
                case esriFieldType.esriFieldTypeGUID:
                    return typeof(Guid);
                case esriFieldType.esriFieldTypeInteger:
                case esriFieldType.esriFieldTypeOID:
                    return typeof(int);
                case esriFieldType.esriFieldTypeRaster:
                    return typeof(byte[]);
                case esriFieldType.esriFieldTypeSingle:
                    return typeof(float);
                case esriFieldType.esriFieldTypeSmallInteger:
                    return typeof(short);
                case esriFieldType.esriFieldTypeString:
                    return typeof(string);
                case esriFieldType.esriFieldTypeXML:
                    return typeof(string);
                default:
                    return typeof(object);
            }
        }
        
        private XPathDocument _XPath
        {
            get
            {
                if (_xPath == null)
                {
                    if (!string.IsNullOrEmpty(_filePath) && File.Exists(_filePath))
                    {
                        _xPath = new XPathDocument(_filePath);
                    }
                }
                return _xPath;
            }
        }

        private string _GetXPathValue(string xpath)
        {
            XPathNavigator navigator = _XPath.CreateNavigator();
            XPathNodeIterator nodes = navigator.Select(xpath);
            if (nodes.Count > 0)
            {
                nodes.MoveNext();
                return nodes.Current.Value;
            }
            else
            {
                string msg = "The given node does not exist in the configuration file:";
                msg += Environment.NewLine;
                msg += xpath;

                throw new ArgumentException(msg);
            }
        }

        private IWorkspace _GetEmgaatsWorkspace()
        {
            IWorkspaceFactory workspaceFactory = new SdeWorkspaceFactoryClass();
            return workspaceFactory.OpenFromFile(_masterSdeFile, ArcMap.Application.hWnd);

            ////IPropertySet propertySet = new PropertySetClass();
            ////propertySet.SetProperty("SERVER", "BESDBDEV1");
            ////propertySet.SetProperty("INSTANCE", "SDE:SQLSERVER:BESDBDEV1");
            ////propertySet.SetProperty("DATABASE", "ASM_MASTER_RPT_SDE");
            ////propertySet.SetProperty("AUTHENTICATION_MODE", "OSA");
            ////propertySet.SetProperty("VERSION", "sde.DEFAULT");
            ////return workspaceFactory.Open(propertySet, ArcMap.Application.hWnd);

            ////IWorkspaceFactory workspaceFactory = new FileGDBWorkspaceFactoryClass();
            ////return workspaceFactory.OpenFromFile(@"E:\EMGAATS\EMGAATS_proxy.gdb", 0);
        }

        private IWorkspace _GetHubWorkspace()
        {
            IPropertySet propertySet = new PropertySetClass();
            propertySet.SetProperty("SERVER", "GISDB1.ROSE.PORTLAND.LOCAL");
            propertySet.SetProperty("INSTANCE", "SDE:SQLSERVER:GISDB1.ROSE.PORTLAND.LOCAL");
            propertySet.SetProperty("DATABASE", "EGH_PUBLIC");
            propertySet.SetProperty("AUTHENTICATION_MODE", "OSA");
            propertySet.SetProperty("VERSION", "sde.DEFAULT");

            IWorkspaceFactory workspaceFactory = new SdeWorkspaceFactoryClass();
            return workspaceFactory.Open(propertySet, ArcMap.Application.hWnd);
        }
    }
}
